#qpy:webapp:StarDevice
#qpy://localhost:8081/

from gTixi import *
from gTixi import wadapter as wad

def welcome():
    led(g=True,r=False,b=False)
    wa.Sleep(0.2)
    led(r=True,g=False,b=False)
    wa.Sleep(0.2)
    led(b=True,r=False,g=False)
    wa.Sleep(0.2)
    led(False,False,False)
    wa.Sleep(0.2)
    led()
    beam(0.5)
    wa.Sleep(0.3)
    led(False,False,False)
welcome()

#关机面板：内容为纯黑屏        
#暂时退出，关闭屏幕，按下任意键重新启动
class offStatus(baseMod):
    def __init__(self,mi):
        baseMod.__init__(self,mi)
    def show(self):
        self.output(" ")

#光照采集器的界面
class LTRLight(baseMod):
    def __init__(self,mi):
        baseMod.__init__(self,mi)
    def show(self):
        self.output(lightSensor())
    def InputKey(self,key):
        if not key:
            return
        if key in "UAPH":
            self.show()
        elif key in "LBNY":
            self.backToMenu()

#异步光照采集器的界面
class LTRAsync(loopMod):
    def __init__(self,mi):
        loopMod.__init__(self,mi)
    def loopUnit(self):
        self.output(lightSensor())

#bme传感器
class BME230(loopMod):
    def __init__(self,mi):
        loopMod.__init__(self,mi)
    def loopUnit(self):
        self.output(bme280())

#管脚连通性
class Pins15(loopMod):
    def __init__(self,mi):
        loopMod.__init__(self,mi)
    def loopUnit(self):
        self.output(pins15()[0])

#管脚连通性-蜂鸣
class Pins15b(loopMod):
    def __init__(self,mi):
        loopMod.__init__(self,mi)
    def loopUnit(self):
        self.output(pins15(True)[0])

#红外传感器
class IR(loopMod):
    def __init__(self,mi):
        loopMod.__init__(self,mi)
        
        self.msg = '红外线检测器:\n{}\n{}\n←B 退出'
        self.lo = '接收中...'
        self.l = ''
        self.output(self.msg.format(self.lo,self.l))
    def strTime(self):
        import time
        f=time.time()
        i=int(f/10)*10
        return '{:.2f}'.format(f-i)       
    def loopUnit(self):
        ir = getIR()
        if ir:
            self.lo=self.l
            self.l=self.strTime()+':{}'.format(ir)
            self.output(self.msg.format(self.lo,self.l))

#蜂鸣器
class Beam(baseMod):
    def __init__(self,mi):
        baseMod.__init__(self,mi)
    def show(self):
        sf = "蜂鸣器\n"
        if self.dev in ["zkb","smp",'wlb']:
            sf += "↑A 蜂鸣\n←B 退出"
        else:
            sf += "设备不支持！\n←B 退出"
        self.output(sf)
    def InputKey(self,key):
        if not key:
            return
        if key in "UAPH":
            beam()
            self.show()
        elif key in "LBNY":
            self.backToMenu()

#帧率测试
class FrameTest(loopMod):
    def __init__(self,mi):
        loopMod.__init__(self,mi)
        self.dur = 0.1
    def loopUnit(self):
        sf='Frame test\ncurrent frame \nrate:{:.2f} fps\nLeft to exit'
        import time
        tic = time.time()
        self.output(sf.format(1.00/self.dur))
        self.dur = time.time()-tic
        if self.dur == 0:
            self.dur = 0.001
        time.sleep(0.001)

#基本信息页
class sysInfo(baseMod):
    def __init__(self,mi):
        baseMod.__init__(self,mi)
        self.pointer = 1
        env = wad.strWaEnv
        self.lines = []
        i=0
        splitLen = 17
        if self.dev == 'zkb':
            splitLen = 15
        while env[i*splitLen:]:
            self.lines.append(env[i*splitLen:i*splitLen+splitLen])
            i += 1
        self.length = len(self.lines)
    def movePointer(self,shift):
        self.pointer = (self.pointer-1 + shift) % (self.length-2)+1
    def show(self):
        menuItem = self.lines
        output = ''
        if len(menuItem)<3:
            for _ in range(3-len(menuItem)):
                menuItem.append('')
        if self.pointer == 0:
            displayList = [0,1,2]
        elif self.pointer == len(menuItem)-1:
            displayList = [len(menuItem)-3,len(menuItem)-2,len(menuItem)-1]
        else:
            displayList = [self.pointer-1,self.pointer,self.pointer+1]
        for (i,item) in enumerate(menuItem):
            if i in displayList:
                output += item + '\n'
        if self.dev == 'zkb':
            output += " A    ←    ↓    ↑    →    B"
        else:
            output = '系统信息\n' + output
        self.output(output)
    def InputKey(self,key):
        if not key:
            return
        if key in "UuH":
            self.movePointer(-1)
            self.show()
        elif key in "DdTAP":
            self.movePointer(1)
            self.show()
        elif key in "LBN":
            self.backToMenu()

class addaTest(baseMod):
    def __init__(self,mi):
        baseMod.__init__(self,mi)
        self.v = 0
    def show(self):
        self.output(adda(self.v))
    def InputKey(self,key):
        if not key:
            return
        if key in "UuH":
            self.v = (self.v+2)%255
            self.show()
        elif key in "DdTAP":
            self.v = (self.v-2)%255
            self.show()
        elif key in "LBN":
            self.backToMenu()

#LED灯切换
class LEDCtrl(baseMod):
    def __init__(self,mi):
        baseMod.__init__(self,mi)
        self.v = 0
    def show(self):
        ctrl = '{:3b}'.format(self.v+8)
        self.output(led(g = ctrl[1]=='1',r = ctrl[2]=='1', b = ctrl[3]=='1'))
    def InputKey(self,key):
        if not key:
            return
        if key in "UuH":
            self.v = (self.v+1)%8
            self.show()
        elif key in "DdTAP":
            self.v = (self.v-1)%8
            self.show()
        elif key in "LBN":
            self.backToMenu()

#GPIO调试
class GPIODebug(baseMod):
    def __init__(self,mi):
        baseMod.__init__(self,mi)
        self.v = 0
    def show(self):
        ctrl = '{:0>2b}'.format(self.v)
        self.output(gpiopin(p0 = ctrl[0]=='1',p1 = ctrl[1]=='1'))
    def InputKey(self,key):
        if not key:
            return
        if key in "UuH":
            self.v = (self.v+1)%4
            self.show()
        elif key in "DdTAP":
            self.v = (self.v-1)%4
            self.show()
        elif key in "LBN":
            self.backToMenu()

class piano(baseMod):
    def __init__(self,mi):
        baseMod.__init__(self,mi)
    def movePointer(self,shift):
        self.pointer = (self.pointer-1 + shift) % (self.length-2)+1
    def show(self):
        if self.dev == 'zkb':
            output = " G                          exit\n ·\n>>>+Star*Piano+<<<\n  C     D     E     F     G     A"
        else:
            output = '星尘协奏曲\n该功能只能运行于掌控版\n按任意键退出。' 
        self.output(output)
    def InputKey(self,key):
        if not key:
            return
        if self.dev == 'zkb':
            import music
            if key in "P":
                music.play('C4:1')
            elif key in "Y":
                music.play('D4:1')
            elif key in "T":
                music.play('E4:1')
            if key in "H":
                music.play('F4:1')
            elif key in "O":
                music.play('G4:1')
            elif key in "N":
                music.play('A4:1')
            elif key in "A":
                music.play('G3:1')
            elif key in "B":
                self.backToMenu()
        else:
            self.backToMenu()

#加法器系列
class Adder(loopPro):
    def __init__(self,mi):
        loopPro.__init__(self,mi)
        self.mod = "once"

    def loop(self,_=None):
        rightConut = 0
        total = 100
        import time
        if self.mod == 'test':
            if self.dev in ['zkb','wlb']:
                self.output("测试中...")
        for i in range(total):
            if not self.stop:
                while self.pause and not self.stop:
                    time.sleep(0.5)
                result, judge = self.testOnce()
                
                
                if judge:
                    rightConut += 1                
                if self.mod == 'display':
                    time.sleep(1)
                    self.output(result)
                elif self.mod == 'test':
                    if not self.dev in ['zkb','wlb']:
                        self.output(result)
            else:
                total = i
                self.stop = False
                self.pause = False
                break
        self.output("正确率：\n{}/{}".format(rightConut,total))
    def testOnce(self):       
        import random
        a=random.randint(0,15)
        b=random.randint(0,15)
        cin=random.randint(0,1)
        
        screenText,_,rout = adder74283((a,b,cin))
        return screenText, rout == a+b+cin

    def show(self):
        if self.mod == 'once':
            self.output(self.testOnce()[0])
            return
        if not self.ctag('board'):
            import threading
            t = threading.Thread(target=self.running)
            t.start()
        else:
            import _thread as th
            th.start_new_thread(self.running,(None,))

        
class AdderDisplay(Adder):
    def __init__(self,mi):
        Adder.__init__(self,mi)
        self.mod = "display"

class AdderTest(Adder):
    def __init__(self,mi):
        Adder.__init__(self,mi)
        self.mod = "test"

#反相器=保存文件
class Inver(loopPro):
    def __init__(self,mi):
        loopPro.__init__(self,mi)
        self.save = True
        if self.ctag('pinmap'):
            self.save = False
    def loop(self,_=None):
        import time
        total = 0
        right = 0
        if self.save:
            saveFile = []
            idx = 1
        
        while True:
            if not self.stop:
                while self.pause and not self.stop:
                    time.sleep(0.5)
                result,right,total,io = inverter(right=right,all=total)
                if self.save:
                    sIn,sOK = io
                    saveFile.append(dict(index=idx,sIn=' '+sIn,sOk=sOK))
                    idx+=1
                    if len(saveFile)>=5e4:
                        self.saveCSV(saveFile,'-invTest.csv')
                        saveFile.clear()
                self.output(result)
            else:
                self.stop = False
                self.pause = False
                if self.save and len(saveFile):
                    self.saveCSV(saveFile,'gateTest.csv')
                    saveFile.clear()
                break
        self.output("正确率：\n{:.3g}%".format(float(right/total*100)))

class InverNoSave(Inver):
    def __init__(self,mi):
        Inver.__init__(self,mi)
        self.save = False


#逻辑门测试
class Logic(loopPro):
    def __init__(self,mi):
        loopPro.__init__(self,mi)
        self.save = True
        if not self.ctag('pinmap'):
            self.save = False
    def loop(self,_=None):
        import time
        if self.save:
            saveFile = []
            idx = 1
        
        while True:
            if not self.stop:
                while self.pause and not self.stop:
                    time.sleep(0.5)
                result,sIn,sOut = logicTest()
                if self.save:
                    saveFile.append(dict(index=idx,sIn=sIn,sOut=sOut))
                    idx+=1
                    if len(saveFile)>=3600:
                        self.saveCSV(saveFile,'-gateTest.csv')
                        saveFile.clear()
                self.output(result)
            else:
                self.stop = False
                self.pause = False
                if self.save and len(saveFile):
                    self.saveCSV(saveFile,'gateTest.csv')
                    saveFile.clear()
                break
        self.output("逻辑门测试\n测试完成！\n ↑继续\n ←退出")


#反相器100
class Inver100(loopPro):
    def __init__(self,mi):
        loopPro.__init__(self,mi)
        self.save = True
        if not self.ctag('pinmap'):
            self.save = False
    def loop(self,_=None):
        import time
        lMessage = []
        diFail = {}
        nRefresh = 5
        count = 0
        tic=time.time()
        ftStart=time.time()
        from datetime import datetime
        lMessage.append(datetime.now().strftime('%y%m%d-%H%M%S') + ': Endurance test started.')
        if self.save:
            sFnStart = self.path + datetime.now().strftime('%y%m%d-%H%M%S')+'-invEndTest.csv'
            saveFile = []
            idx = 1
        sf='7404反相器测试\n R: {:.1f} sps  \n N: {}K↑暂停\n E:{} ←退出 '
        while True:
            if not self.stop:
                while self.pause and not self.stop:
                    time.sleep(0.5)
                if count%nRefresh == 0:
                    toc=time.time()-tic
                    tic=time.time()
                    if toc == 0:
                        toc = 0.001
                    rate=nRefresh*100/toc
                    gc = invqe()
                    if self.save:
                        saveFile.append(dict(index=idx,gc=gc))
                        idx+=1
                    sGood = []
                    for i,p in enumerate(gc):
                        if p>0.9:
                            sGood.append('O')
                        else:
                            sGood.append('X')
                            if i not in diFail:
                                diFail[i]=count*100
                                lMessage.append(datetime.now().strftime('%y%m%d-%H%M%S') + ': Inverter#{} failed at {}'.format(i,diFail[i]))
                    sGood=''.join(sGood)
                    if sGood=='XXXXXX':
                        lMessage.append('All devices failed in {} tests, test ended.'.format(count*100))
                        #self.stop = True
                    self.output(sf.format(rate,count/10,sGood))                    
                    if self.save and len(saveFile)>=5e4:
                        import pandas as pd
                        df = pd.DataFrame(saveFile)
                        fn = self.path + datetime.now().strftime('%y%m%d-%H%M%S')+'-invEndTest.csv'
                        df.to_csv(fn,index=False)
                        lMessage.append(datetime.now().strftime('%y%m%d-%H%M%S')+': {}saved'.format(fn))
                        saveFile.clear()  
                count+=1
            else:
                if self.save and len(saveFile)>0:
                    self.saveCSV(saveFile,'-invEndTest.csv')
                    lMessage.append(datetime.now().strftime('%y%m%d-%H%M%S')+': {}saved'.format(fn))
                    saveFile.clear()
                self.stop = False
                self.pause = False
                break
        ftRun=time.time()-ftStart
        self.output('7404反相器测试\n测试结束!\n运行{:.4f}小时。\n ↑开始 ←退出'.format(ftRun/3600))
        lMessage.append(datetime.now().strftime('%y%m%d-%H%M%S')+': Terminated, run for {:.4f} hours.\r\n diFail: {}'.format(ftRun/3600,diFail))        
        if self.save:
            self.saveTXT('\n'.join(lMessage),'-invEndTest.txt')
        print('\n'.join(lMessage))


#菜单功能
#因为mpython下顺序会出错，所以添加了序号与排序函数
devMenu = {
    "1.器件分析":{
        "1.加法器":{
            "1.单次加法":Adder,
            "2.100次加法":AdderDisplay,
            "3.100次快速":AdderTest,
            "4.返回":0        
        },
        "2.反相器":{
            "1.反相器100":Inver100,
            "2.仅测试":InverNoSave,
            "3.旧版保存":Inver,
            "4.返回":0
        },
        "3.管脚连通":{
            "1.标准版":Pins15,
            "2.蜂鸣版":Pins15b,
            "3.返回":0
        },
        "4.逻辑门测试":Logic,
        "5.返回":0
    },
    "2.系统信息":{
        "1.基本信息":sysInfo,
        "2.运行帧率":FrameTest,
        "3.ADDA":addaTest,
        "4.GPIO":GPIODebug,
        "5.返回":0
    },
    "3.模拟":{
        "1.光照":{
            "1.单次光照":LTRLight,
            "2.异步光照":LTRAsync,
            "3.返回":0
        },
        "2.BME230":BME230,
        "3.红外":IR,
        "4.钢琴":piano,
        "5.蜂鸣器":Beam,
        "6.LED":LEDCtrl,
        "7.返回":0
    },
    "4.退出":offStatus
}

starMessager.workModule = menu(starMessager,devMenu)
starMessager.workModule.show()
if starMessager.shipei.ctag('web'):
    wsServer.start()
if starMessager.shipei.ctag('web') and __name__ == "__main__":
    httpServer.start()
